package com.partsoft.dits.ant;

import java.io.File;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;

import org.apache.commons.dbcp.BasicDataSource;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.ProjectHelper;
import org.apache.tools.ant.Task;
import org.apache.tools.ant.types.Path;
import org.springframework.util.ReflectionUtils;

import com.partsoft.dits.dsm.DataSourceDescriptor;
import com.partsoft.dits.dsm.DefaultContextDataSourceManager;
import com.partsoft.utils.ClassUtils;
import com.partsoft.utils.StringUtils;

public class DataSourceManagerTask extends Task {

	public static final String TaskElement = "datasource-manager";

	private String command;

	private String configLocation;

	private String dataSourceName;

	private String jdbcDriver;

	private String jdbcURL;

	private String dbUser;

	private String dbPassword;

	private String validate;

	private String classpath;

	public String getCommand() {
		return command;
	}

	public void setCommand(String command) {
		this.command = command;
	}

	public String getDataSourceName() {
		return dataSourceName;
	}

	public void setDataSourceName(String dataSourceName) {
		this.dataSourceName = dataSourceName;
	}

	public void setDatasource_name(String dataSourceName) {
		this.dataSourceName = dataSourceName;
	}

	public String getJdbcDriver() {
		return jdbcDriver;
	}

	public void setJdbcDriver(String jdbcDriver) {
		this.jdbcDriver = jdbcDriver;
	}

	public void setJdbc_driver(String jdbcDriver) {
		this.jdbcDriver = jdbcDriver;
	}

	public String getJdbcURL() {
		return jdbcURL;
	}

	public String getClasspath() {
		return classpath;
	}

	public void setClasspath(String classpath) {
		this.classpath = classpath;
	}

	public void setJdbcURL(String jdbcURL) {
		this.jdbcURL = jdbcURL;
	}

	public void setJdbc_url(String jdbcURL) {
		this.jdbcURL = jdbcURL;
	}

	public String getDbUser() {
		return dbUser;
	}

	public void setDbUser(String dbUser) {
		this.dbUser = dbUser;
	}

	public void setDb_user(String dbUser) {
		this.dbUser = dbUser;
	}

	public String getDbPassword() {
		return dbPassword;
	}

	public void setDbPassword(String dbPassword) {
		this.dbPassword = dbPassword;
	}

	public void setDb_password(String dbPassword) {
		this.dbPassword = dbPassword;
	}

	public String getConfigLocation() {
		return configLocation;
	}

	public void setConfigLocation(String configLocation) {
		this.configLocation = configLocation;
	}

	public void setConfig_location(String configLocation) {
		this.configLocation = configLocation;
	}

	public String getValidate() {
		return validate;
	}

	public void setValidate(String validate) {
		this.validate = validate;
	}

	@Override
	public void execute() throws BuildException {
		ProjectHelper helper = (ProjectHelper) getProject().getReference("ant.projectHelper");
		if (helper == null) {
			throw new BuildException(String.format("%s requires support in ProjectHelper", TaskElement));
		}

		boolean validate_ds = true;
		if (StringUtils.hasText(getValidate()) && !getValidate().startsWith("${")) {
			validate_ds = !getValidate().equalsIgnoreCase("false");
		}

		String config_file_path = getConfigLocation();

		if (!StringUtils.hasText(getDataSourceName()) || getDataSourceName().startsWith("${")) {
			throw new BuildException("must set name param");
		}

		if (getCommand().equalsIgnoreCase("import")) {
			if (!StringUtils.hasText(getJdbcDriver()) || getJdbcDriver().startsWith("${")) {
				throw new BuildException("must set driver param");
			}

			if (!StringUtils.hasText(getJdbcURL()) || getJdbcURL().startsWith("${")) {
				throw new BuildException("must set url param");
			}

			if (!StringUtils.hasText(getDbUser()) || getDbUser().startsWith("${")) {
				throw new BuildException("must set user param");
			}

			if (!StringUtils.hasText(getDbPassword()) || getDbPassword().startsWith("${")) {
				throw new BuildException("must set pwd param");
			}
		}

		if (!StringUtils.hasText(config_file_path) || config_file_path.startsWith("${")) {
			throw new BuildException("must set config param");
		}

		if (!config_file_path.startsWith("file:/")) {
			if (config_file_path.startsWith("\\") || config_file_path.startsWith("/")) {
				config_file_path = "file:/" + config_file_path;
			} else if (config_file_path.startsWith("./") || config_file_path.startsWith(".\\")) {
				config_file_path = "file:/" + new File("").getAbsolutePath() + File.separator
						+ config_file_path.substring(2);
			} else {
				config_file_path = "file:/" + new File("").getAbsolutePath() + File.separator + config_file_path;
			}
		}

		log("command=" + getCommand());
		log("name=" + getDataSourceName());
		if (getCommand().equalsIgnoreCase("import")) {
			log("driver=" + getJdbcDriver());
			log("url=" + getJdbcURL());
			log("user=" + getDbUser());
			log("pwd=" + getDbPassword());
			log("validate=" + validate_ds);
		} 
		log("config=" + config_file_path);

		ClassLoader old_classload = Thread.currentThread().getContextClassLoader();

		Method classload_addurl_method = null;
		boolean classload_addurl_method_oac = false;
		if (old_classload instanceof URLClassLoader) {
			classload_addurl_method = ReflectionUtils.findMethod(URLClassLoader.class, "addURL", URL.class);
		}
		classload_addurl_method_oac = classload_addurl_method.isAccessible();
		classload_addurl_method.setAccessible(true);
		String[] class_jar_filenames = getClasspath().split(File.pathSeparator);
		List<URL> class_jar_urls = new LinkedList<URL>();
		if (class_jar_filenames != null) {
			for (String class_jar_filename : class_jar_filenames) {
				File class_jarfile = new File(class_jar_filename);
				try {
					URL be_add_jar = class_jarfile.toURI().toURL();
					if (classload_addurl_method != null) {
						try {
							classload_addurl_method.invoke(old_classload, be_add_jar);
						} catch (Throwable e) {
							throw new BuildException(e);
						}
					} else {
						class_jar_urls.add(be_add_jar);
					}
				} catch (MalformedURLException e) {
					throw new BuildException(e.getMessage(), e);
				}
			}
		}

		if (class_jar_urls.size() > 0) {
			URLClassLoader current_classloader = new URLClassLoader(class_jar_urls.toArray(new URL[0]), old_classload);
			Thread.currentThread().setContextClassLoader(current_classloader);
		} else {
			classload_addurl_method.setAccessible(classload_addurl_method_oac);
		}

		DataSourceDescriptor dsd = new DataSourceDescriptor(getDataSourceName(), getJdbcDriver(), getJdbcURL(),
				getDbUser(), getDbPassword());

		DefaultContextDataSourceManager dsmanager = null;
		BasicDataSource ds = null;
		try {
			dsmanager = new DefaultContextDataSourceManager(config_file_path, true);
			dsmanager.setValidateDescriptor(validate_ds);
			dsmanager.initContext();
			if (getCommand().equalsIgnoreCase("import")) {
				if (dsmanager.hasDataSource(getDataSourceName())) {
					dsmanager.modifyDescriptor(dsd);
				} else {
					dsmanager.addDescriptor(dsd);
				}
			} else if (getCommand().equalsIgnoreCase("remove")) {
				dsmanager.removeDescriptor(dsd.getName());
			}
		} catch (Throwable e) {
			System.out.println(String.format("import datasource error: %s", e.getMessage()));
			throw new BuildException(e);
		} finally {
			if (dsmanager != null) {
				dsmanager.finiContext();
			}
			Thread.currentThread().setContextClassLoader(old_classload);
		}

	}

}
